<?php
namespace coreLib\RouteClass;
use coreLib\ErrorClass\Error;

/**
 * 路由操作类
 * @desc 用于将请求的路由进行分解，获取到响应的请求地址与请求参数
 **/
class Route{

	/*
	 * 私有属性
	 */
	private   $db;						 //数据库操作对象
	private   $nowRequestInterfaceInfo; //当前准备处理的接口
	protected $pathInfo;				 //pathinfo请求路径
	protected $httpHost;	    		 //主机地址
	protected $userAgent;				 //访问设备信息
	protected $serverPort;				 //主机端口
	protected $requestScheme;   		 //请求协议
	
	/*
	 * 向外暴露属性
	 */
	public 	  $restfulParams;			 //restful接口请求参数
	public    $bodyParams;			     //body请求参数
	public 	  $requestInterface;  		 //接口请求地址
	public 	  $reuqestFilePath;		     //接口请求的完整文件路径
	public 	  $requestToken;			 //请求token信息
	public 	  $requestServerToken;	     //服务器请求token信息
	public 	  $requestServerAppId;	     //请求服务器的appid
	public    $requestUri;				 //请求完整路径
	public 	  $requestMethod;			 //请求方式
	public    $pathInfoName ;             //接口文件夹
	public    $pathInfoLibUrl ;             //请求接口对应modelURL

	/*
	 * 构造函数
	 * @params {$db} 数据库操作对象
	 * @return 没有返回值
	 */
	function __construct($db){

		//数据库操作对象
		$this->db  			 = $db;

		//通过SERVER全局数组获取相关路由信息
		$this->pathInfo      = $_SERVER['PATH_INFO'] ? $_SERVER['PATH_INFO']: $_SERVER['QUERY_STRING'];
		$this->httpHost      = $_SERVER['HTTP_HOST'];
		$this->userAgent     = $_SERVER['HTTP_USER_AGENT'];
		$this->serverPort    = $_SERVER['SERVER_PORT'];
		$this->requestUri    = $_SERVER['REQUEST_URI'];
		$this->requestMethod = $_SERVER['REQUEST_METHOD'];
		$this->requestScheme = $_SERVER['REQUEST_SCHEME'];


		//获取用户header头文件信息（一般将token存储在头部）
		$this->requestToken = $_SERVER['HTTP_X_TOKEN'];

		//获取服务器token信息
		$this->requestServerToken = $_SERVER['HTTP_X_SERVERTOKEN'] ? $_SERVER['HTTP_X_SERVERTOKEN'] : NULL;

		//请求服务器的appid
		$this->requestServerAppId = $_SERVER['HTTP_X_APPID'] ? $_SERVER['HTTP_X_APPID'] : NULL;


		//判断是否为一个合法请求
		$this->isConformityUrl();

		//查看访问的接口是否有效（存在）
		$this->isInterfaceValid();

		//进行路由解析
		$this->URIparse();	

		//进行服务器地址与接口白名单验证
		$this->serverVerification();

		//查看接口文件是否存在
		$this->isInterfaceExists();

		//查看该接口是否需要验证，如果需要则进行token验证
		$this->tokenValid();

		$this->urlPathInfoLib();

	}

	
	/*
	*获得请求文件夹对应lib 层 路径
	*
	*@return 没有返回值
	*/

	protected function urlPathInfoLib(){
		
		$paramsStr = trim($this->nowRequestInterfaceInfo[0],"/");
		
		$arr=explode( '/',$paramsStr);

		$this->pathInfoName=$arr[0];

		//拼凑路径
		$url='./lib/'.$arr[0].'.Class.php';
		
		file_exists($url)?$this->pathInfoLibUrl=$url:$this->pathInfoLibUrl=false;

	}             



	/*
	 * 路由解析
	 * @return 没有返回值
	 */
	protected function URIparse(){

		//获取除去接口外的参数字符串
		$paramsStr = str_replace($this->nowRequestInterfaceInfo[0],"",$this->pathInfo);
		$paramsStr = trim($paramsStr,"/");
		
		//保存最终参数结果
		$paramsRs  = array();

		//如果存在参数
		if(!empty($paramsStr)){

			//判断参数的格式是否正确
			if(preg_match("/^[0-9]+[a-zA-Z_]?/",$paramsStr)){
				Error::Result($this->visitorId,"REQUEST_ERROR",$GLOBALS['errorMsg']['paramsFormatError']);
			}

			//将可能数据分解成为参数数组
			$paramsArr = explode("/",$paramsStr);

			
			for($i=0;$i<count($paramsArr);$i+=2){

				$tempKey   = $paramsArr[$i];

				//判断参数key是否为数字，如果为数组，则为无效key
				if(intval($tempKey)){
				   Error::Result("REQUEST_ERROR",$GLOBALS['errorMsg']['paramsFormatError']);
				}

				$tempValue = $paramsArr[$i+1];
				array_push($paramsRs,$tempKey."=".$tempValue);

			}
		}

		//判断是否有参数
		if(count($paramsRs)>0){

			//临时存放参数解析最终数组
			$tempParamsArr = [];

			//将参数处理成为关联数组（参数名 = 参数值）的格式
			for($i=0;$i<count($paramsRs);$i++){

				$param = explode("=",$paramsRs[$i]);
				$tempParamsArr[$param[0]] = $param[1];
			}

			//保存restful请求参数
			$this->restfulParams = $tempParamsArr;
		}

		//保存请求地址
		$this->requestInterface = $this->nowRequestInterfaceInfo[0];
		
		//保存body请求参数
		$this->bodyParams = $_POST;
	
	}

	/*
	 * 进行服务器地址与接口白名单验证
	 * @return 没有返回值
	 */
	protected function serverVerification(){

		//获取本地访问的接口名称
		$nowApi = $this->nowRequestInterfaceInfo[0];

		
		//查看该接口是否在接口白名单中
		if(!(in_array($nowApi,$GLOBALS['interfaceName']))){

			//获取请求服务器的地址+端口号
			$serverAddress = $this->requestScheme."://".$this->httpHost;

			//获取服务器白名单数组
			$whiteListArr = $GLOBALS['serverIP'];

			//判断是否在服务器ip白名单中存在
			if(!(in_array($serverAddress,$whiteListArr))){
				 Error::Result("REQUEST_ERROR",$GLOBALS['errorMsg']['noServerAuthorizationError']); 	
			}
		}
	}


	/*
	 * 判断请求接口是否合法
	 * @return 没有返回值
	 */
	protected function isConformityUrl(){

		//判断请求方式
		if(!(in_array($this->requestMethod,$GLOBALS['settings']['method']))){
			Error::Result("REQUEST_ERROR",$GLOBALS['errorMsg']['methodError']);
		}

		//判断请求接口是否为空
		if(empty($this->pathInfo)){
			Error::Result("REQUEST_ERROR",$GLOBALS['errorMsg']['interfaceEmptyError']);
		}

	}


	/*
	 * 查看该接口是否有效
	 * @return 没有返回值
	 */
	protected function isInterfaceValid(){

		//匹配的接口数
		$matchCount = 0;
		
		$tempPathArr = explode("/",$this->pathInfo);
		$tempPathStr = "/".$tempPathArr[1]."/".$tempPathArr[2];

		//遍历配置文件中的接口
		foreach($GLOBALS['interfacePrivilege'] as $key=>$value){

			//查看访问的pathinfo是否存在于接口配置文件
			if($tempPathStr == $key){

				//存在，则保存准备处理的接口名称与接口权限
				$this->nowRequestInterfaceInfo = [$key,$value];

				//累计匹配数
				$matchCount++;
			}
		}	
		
		//不存在该接口
		if($matchCount == 0){
			Error::Result("REQUEST_ERROR",$GLOBALS['errorMsg']['interfaceNoneError']);	
		}
		
	}

	/*
	 * 查看接口文件是否存在
	 * @return 没有返回值
	 */
	protected function isInterfaceExists(){


		//拼接需要访问的文件
		$interfacePath = "./business".$this->requestInterface.".php";

		

		//查看该文件是否存在
		if(!file_exists($interfacePath)){
			Error::Result("REQUEST_ERROR",$GLOBALS['errorMsg']['interfaceFileNoneError']);
		}

		//保存最终访问的文件路径
		$this->reuqestFilePath = $interfacePath;

	}

	/*
	 * 查看该接口是否需要验证，如果需要则进行token验证
	 * @return 没有返回值
	 */
	protected function tokenValid(){

		//获取验证方式以及接口所需的请求方式
		$interfaceInfo = explode("|",$this->nowRequestInterfaceInfo[1]);

		//判断接口的请求方式是否正确
		if($interfaceInfo[1] != 'ALL'){
			if($this->requestMethod != $interfaceInfo[1]){
			   Error::Result("REQUEST_ERROR",$GLOBALS['errorMsg']['noMethodError']);	
			}
		};

		//不验证模式	
		if($interfaceInfo[0] == "no"){


		//token验证模式
		}else if($interfaceInfo[0] == "token"){

			//进行token验证,在redis与数据库中进行验证
			$this->validationToken();

		//系统错误
		}else{
			Error::Result("SYSTEM_ERROR",$GLOBALS['errorMsg']['interfaceProvilegeError']);	
		}
		
	}

	/*
	 * token验证操作
	 * @return 没有返回值
	 */
	protected function validationToken(){
		
		//判断是否传递了token参数
		if($this->requestToken == NULL){
		   Error::Result("SYSTEM_ERROR",$GLOBALS['errorMsg']['noTokenError']);		
		}

		//1.查询Redis中是否有该token键值,如果有则说明该token未过期
		if(!($this->db->redisDB->getData($this->requestToken))){
  		    
  		    //调用存储过程参数
  		    $proceArr = [
  		    	$this->requestToken
  		    ];

  		    //构建存储过程参数
  		    $this->db->mysqlDB->setProceduerParams($proceArr,"R");

  		    //去MYSQL数据库中验证TOKEN是否过期
   			$this->db->mysqlDB->execProceduer("EC_CHECK_TOKEN_IS_EXPIRES",$proceArr);

   			//取出配置文件中的TOKEN过期时间
   			$tokenValidTime = $GLOBALS['settings']['tokenValidity'];

   			//token未过期，将该token重新存储进redis缓存数据库
   			$this->db->redisDB->setData($this->requestToken,TRUE);
   			$this->db->redisDB->setExpire($this->requestToken,$tokenValidTime);

		}

	}

}